package org.lts.yuqueexportor.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.lts.yuqueexportor.config.YuqueConfig;
import org.lts.yuqueexportor.entity.ExportResult;
import org.lts.yuqueexportor.entity.YuqueBook;
import org.lts.yuqueexportor.entity.YuqueDoc;
import org.lts.yuqueexportor.entity.YuqueGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 语雀API服务
 * 提供与语雀平台交互的核心功能
 */
@Slf4j
@Service
public class YuqueService {

    @Autowired
    private YuqueConfig yuqueConfig;

    @Autowired
    private CloseableHttpClient httpClient;

    /**
     * 获取用户加入的所有团队
     *
     * @return 团队列表
     * @throws IOException 网络请求异常
     */
    public List<YuqueGroup> getUserGroups() throws IOException {
        String url = yuqueConfig.getBaseUrl() + "/mine/groups";
        log.info("正在获取用户团队列表...");

        String responseString = executeGet(url);
        JSONObject jsonObject = JSON.parseObject(responseString);
        JSONArray dataArray = jsonObject.getJSONArray("data");

        List<YuqueGroup> groups = new ArrayList<>();
        if (dataArray != null) {
            for (int i = 0; i < dataArray.size(); i++) {
                YuqueGroup group = dataArray.getObject(i, YuqueGroup.class);
                groups.add(group);
                log.debug("  - 团队: {} ({})", group.getName(), group.getLogin());
            }
        }

        log.info("成功获取 {} 个团队", groups.size());
        return groups;
    }

    /**
     * 获取个人知识库列表
     *
     * @return 个人知识库列表
     * @throws IOException 网络请求异常
     */
    public List<YuqueBook> getPersonalBooks() throws IOException {
        String url = yuqueConfig.getBaseUrl() + "/mine/personal_books";
        log.info("正在获取个人知识库列表...");

        String responseString = executeGet(url);
        JSONObject jsonObject = JSON.parseObject(responseString);
        JSONArray dataArray = jsonObject.getJSONArray("data");

        List<YuqueBook> books = new ArrayList<>();
        if (dataArray != null) {
            for (int i = 0; i < dataArray.size(); i++) {
                JSONObject bookJson = dataArray.getJSONObject(i);
                YuqueBook book = dataArray.getObject(i, YuqueBook.class);

                // 手动构造namespace: API返回的数据没有namespace字段
                if (book.getNamespace() == null || book.getNamespace().isEmpty()) {
                    JSONObject userObj = bookJson.getJSONObject("user");
                    if (userObj != null && book.getSlug() != null) {
                        String userLogin = userObj.getString("login");
                        String namespace = userLogin + "/" + book.getSlug();
                        book.setNamespace(namespace);
                        log.debug("构造个人空间namespace: {}", namespace);
                    }
                }

                books.add(book);
                log.debug("  - 知识库: {} (ID: {}, type: {}, namespace: {})",
                        book.getName(), book.getId(), book.getType(), book.getNamespace());
            }
        }

        log.info("成功获取 {} 个个人知识库", books.size());
        return books;
    }

    /**
     * 获取团队的知识库列表
     * API文档: https://www.yuque.com/yuque/developer/api#785a3731
     *
     * @param group 团队对象
     * @return 知识库列表
     * @throws IOException 网络请求异常
     */
    public List<YuqueBook> getGroupBooks(YuqueGroup group) throws IOException {
        String url = yuqueConfig.getBaseUrl() + "/groups/" + group.getId() + "/bookstacks";
        log.info("正在获取团队 [{}] 的知识库列表...", group.getName());

        String responseString = executeGet(url);
        JSONObject jsonObject = JSON.parseObject(responseString);
        JSONArray dataArray = jsonObject.getJSONArray("data");

        List<YuqueBook> books = new ArrayList<>();
        if (dataArray != null) {
            // bookstacks API返回的是分组结构,每个分组包含books数组
            for (int i = 0; i < dataArray.size(); i++) {
                JSONObject bookstack = dataArray.getJSONObject(i);
                JSONArray bookArr = bookstack.getJSONArray("books");

                if (bookArr != null) {
                    for (int j = 0; j < bookArr.size(); j++) {
                        JSONObject bookJson = bookArr.getJSONObject(j);
                        log.debug("知识库原始JSON: {}", bookJson.toString());

                        YuqueBook book = bookArr.getObject(j, YuqueBook.class);

                        // 手动构造namespace: API返回的数据没有namespace字段
                        // namespace格式为: {user_login}/{book_slug}
                        if (book.getNamespace() == null || book.getNamespace().isEmpty()) {
                            JSONObject userObj = bookJson.getJSONObject("user");
                            if (userObj != null && book.getSlug() != null) {
                                String userLogin = userObj.getString("login");
                                String namespace = userLogin + "/" + book.getSlug();
                                book.setNamespace(namespace);
                                log.debug("构造namespace: {}", namespace);
                            }
                        }

                        // 从summary字段提取文档列表(用于受限空间的workaround)
                        JSONArray summaryArray = bookJson.getJSONArray("summary");
                        List<YuqueDoc> summaryDocs = new ArrayList<>();
                        if (summaryArray != null) {
                            for (int k = 0; k < summaryArray.size(); k++) {
                                JSONObject summaryItem = summaryArray.getJSONObject(k);
                                String itemType = summaryItem.getString("type");

                                // 只提取Doc类型的文档
                                if ("Doc".equals(itemType)) {
                                    YuqueDoc doc = new YuqueDoc();
                                    doc.setId(summaryItem.getLong("id"));
                                    doc.setSlug(summaryItem.getString("slug"));
                                    doc.setTitle(summaryItem.getString("title"));
                                    doc.setBookId(summaryItem.getLong("book_id"));
                                    doc.setFormat(summaryItem.getString("format"));
                                    summaryDocs.add(doc);
                                }
                            }
                        }

                        // 将summary文档列表存储到一个临时Map中,供后续使用
                        // 这里我们需要一个方法来缓存这些文档
                        if (!summaryDocs.isEmpty()) {
                            log.debug("  - 从summary中提取到 {} 个文档", summaryDocs.size());
                            book.setSummaryDocs(summaryDocs); // 需要在YuqueBook中添加这个字段
                        }

                        books.add(book);
                        log.debug("  - 知识库: {} (ID: {}, type: {}, namespace: {})",
                                book.getName(), book.getId(), book.getType(), book.getNamespace());
                    }
                }
            }
        }

        log.info("团队 [{}] 共有 {} 个知识库", group.getName(), books.size());
        return books;
    }

    /**
     * 获取知识库下的所有文档
     * API: GET /repos/{namespace}/docs
     *
     * @param book 知识库对象
     * @return 文档列表
     * @throws IOException 网络请求异常
     */
    public List<YuqueDoc> getBookDocs(YuqueBook book) throws IOException {
        log.info("正在获取知识库 [{}] 的文档列表...", book.getName());

        // 优先使用summary中缓存的文档列表(适用于受限空间)
        if (book.getSummaryDocs() != null && !book.getSummaryDocs().isEmpty()) {
            log.info("使用summary缓存的文档列表,共 {} 个文档", book.getSummaryDocs().size());
            return book.getSummaryDocs();
        }

        // 如果没有summary数据,尝试通过API获取(可能会因为受限而失败)
        String url = yuqueConfig.getBaseUrl() + "/docs?book_id=" + book.getId();
        log.debug("文档列表API URL: {}", url);

        try {
            String responseString = executeGet(url);
            JSONObject jsonObject = JSON.parseObject(responseString);

            // 检查是否返回错误
            Integer status = jsonObject.getInteger("status");
            if (status != null && status != 200) {
                log.warn("知识库 [{}] - API返回错误: {}", book.getName(), jsonObject.getString("message"));
                return new ArrayList<>();
            }

            JSONArray dataArray = jsonObject.getJSONArray("data");
            List<YuqueDoc> docs = new ArrayList<>();

            if (dataArray != null) {
                for (int i = 0; i < dataArray.size(); i++) {
                    JSONObject docJson = dataArray.getJSONObject(i);
                    String type = docJson.getString("type");

                    // 只导出类型为DOC的文档
                    if ("DOC".equals(type) || "Doc".equals(type)) {
                        YuqueDoc doc = new YuqueDoc();
                        doc.setId(docJson.getLong("id"));
                        doc.setSlug(docJson.getString("slug"));
                        doc.setTitle(docJson.getString("title"));
                        doc.setBookId(docJson.getLong("book_id"));
                        doc.setFormat(docJson.getString("format"));
                        docs.add(doc);
                    }
                }
            }

            log.info("知识库 [{}] 共有 {} 个文档", book.getName(), docs.size());
            return docs;

        } catch (Exception e) {
            log.error("获取知识库 [{}] 文档列表失败: {}", book.getName(), e.getMessage());
            return new ArrayList<>();
        }
    }

    /**
     * 获取文档详情并返回Markdown内容
     * 支持多种API端点格式
     * API: GET /api/docs/{slug}?book_id={bookId}
     * 或: GET /api/repos/{namespace}/docs/{slug}
     *
     * @param doc 文档对象
     * @param book 所属知识库对象
     * @return 导出结果,包含Markdown内容
     * @throws IOException 网络请求异常
     */
    public ExportResult exportDoc(YuqueDoc doc, YuqueBook book) throws IOException {
        // 使用文档详情API获取内容
        String docSlug = (doc.getSlug() != null && !doc.getSlug().isEmpty())
                ? doc.getSlug() : String.valueOf(doc.getId());

        // 尝试两种API格式
        String url1 = String.format("%s/docs/%s?book_id=%d&include=body,body_html,body_lake,body_draft",
                yuqueConfig.getBaseUrl(), docSlug, book.getId());
        String url2 = String.format("%s/repos/%s/docs/%s",
                yuqueConfig.getBaseUrl(), book.getNamespace(), docSlug);

        String url = url1;  // 默认使用第一种格式

        log.debug("正在获取文档内容: [{}]", doc.getTitle());
        log.debug("  - URL: {}", url);

        try {
            String responseString = executeGet(url);
            JSONObject jsonObject = JSON.parseObject(responseString);

            // 检查是否返回错误
            Integer status = jsonObject.getInteger("status");
            if (status != null && status != 200) {
                log.warn("获取文档 [{}] 失败: {}", doc.getTitle(), jsonObject.getString("message"));
                ExportResult result = new ExportResult();
                result.setStatus("failed");
                return result;
            }

            JSONObject dataObject = jsonObject.getJSONObject("data");
            ExportResult result = new ExportResult();

            if (dataObject != null) {
                // 尝试获取不同格式的内容
                String markdownContent = null;

                // 调试: 打印所有可用字段和详细信息
                log.debug("【调试】API 返回的文档字段列表:");
                log.debug("  API URL: {}", url);
                dataObject.keySet().forEach(key -> {
                    Object value = dataObject.get(key);
                    if (value instanceof String) {
                        String strValue = (String) value;
                        int length = strValue.length();
                        if (length > 100) {
                            log.debug("  - {} (长度: {} 字符)", key, length);
                        } else {
                            log.debug("  - {} = {}", key, strValue);
                        }
                    } else if (value instanceof JSONObject || value instanceof JSONArray) {
                        log.debug("  - {} (JSON对象/数组)", key);
                    }
                });
                log.debug("【调试】完整的data对象: {}", dataObject.toJSONString());

                // 1. 优先尝试body_lake字段(Lake格式的Markdown)
                String bodyLake = dataObject.getString("body_lake");
                if (bodyLake != null && !bodyLake.isEmpty()) {
                    markdownContent = convertLakeToMarkdown(bodyLake);
                    log.debug("✓ 使用body_lake字段(Lake格式), 内容长度: {}", markdownContent.length());
                } else {
                    log.debug("✗ body_lake字段为空或不存在");
                }

                // 2. 如果没有body_lake,尝试body字段
                if (markdownContent == null || markdownContent.isEmpty()) {
                    markdownContent = dataObject.getString("body");
                    if (markdownContent != null && !markdownContent.isEmpty()) {
                        log.debug("✓ 使用body字段, 内容长度: {}", markdownContent.length());
                    } else {
                        log.debug("✗ body字段为空或不存在");
                    }
                }

                // 3. 如果还是没有,尝试body_draft字段
                if (markdownContent == null || markdownContent.isEmpty()) {
                    markdownContent = dataObject.getString("body_draft");
                    if (markdownContent != null && !markdownContent.isEmpty()) {
                        log.debug("✓ 使用body_draft字段, 内容长度: {}", markdownContent.length());
                    } else {
                        log.debug("✗ body_draft字段为空或不存在");
                    }
                }

                // 4. 尝试body_html字段(HTML格式)
                if (markdownContent == null || markdownContent.isEmpty()) {
                    String bodyHtml = dataObject.getString("body_html");
                    if (bodyHtml != null && !bodyHtml.isEmpty()) {
                        markdownContent = convertHtmlToMarkdown(bodyHtml);
                        log.debug("✓ 使用body_html字段并转换为Markdown, 内容长度: {}", markdownContent.length());
                    } else {
                        log.debug("✗ body_html字段为空或不存在");
                    }
                }

                // 5. 尝试直接获取content字段(Lake格式的内容)
                if (markdownContent == null || markdownContent.isEmpty()) {
                    String content = dataObject.getString("content");
                    if (content != null && !content.isEmpty()) {
                        markdownContent = convertLakeToMarkdown(content);
                        log.debug("✓ 使用content字段(Lake格式), 内容长度: {}", markdownContent.length());
                    } else {
                        log.debug("✗ content字段为空或不存在");
                    }
                }

                // 6. 尝试从嵌套对象中提取内容
                if (markdownContent == null || markdownContent.isEmpty()) {
                    JSONObject contentObj = dataObject.getJSONObject("content");
                    if (contentObj != null) {
                        markdownContent = contentObj.getString("body");
                        if (markdownContent != null && !markdownContent.isEmpty()) {
                            log.debug("✓ 从content对象中提取body字段, 内容长度: {}", markdownContent.length());
                        }
                    }
                }

                // 6. 如果内容仍为空,生成默认内容(标题 + 元数据)
                if (markdownContent == null || markdownContent.isEmpty()) {
                    log.debug("【警告】文档内容为空,生成默认内容");
                    log.debug("【诊断信息】");
                    log.debug("  - 尝试过的API端点: {}", url);
                    log.debug("  - 知识库namespace: {}", book.getNamespace());
                    log.debug("  - 文档slug: {}", docSlug);
                    log.debug("  - API返回的所有字段都为空");
                    log.debug("  【建议】如果内容在语雀中可见,可能是:");
                    log.debug("    1. API端点格式不正确,需要使用 /repos/{namespace}/docs/{slug} 格式");
                    log.debug("    2. 需要特殊的权限或参数");
                    log.debug("    3. 需要调用其他API获取完整内容");
                    log.debug("  API返回的完整data对象: {}", dataObject.toJSONString());
                    markdownContent = generateDefaultContent(dataObject, doc);
                }

                if (markdownContent != null && !markdownContent.isEmpty()) {
                    result.setUrl("direct_markdown");
                    result.setTaskId(markdownContent);
                    result.setStatus("success");
                    log.debug("✓ 成功获取文档 [{}] 内容,长度: {} 字符", doc.getTitle(), markdownContent.length());
                } else {
                    log.warn("✗ 文档 [{}] 的内容为空,无法导出", doc.getTitle());
                    result.setStatus("empty_body");
                }
            } else {
                log.warn("✗ 获取文档 [{}] 失败,data字段为空", doc.getTitle());
                result.setStatus("failed");
            }

            return result;

        } catch (Exception e) {
            log.error("获取文档 [{}] 异常: {}", doc.getTitle(), e.getMessage());
            ExportResult result = new ExportResult();
            result.setStatus("failed");
            return result;
        }
    }

    /**
     * 将Lake格式转换为Markdown
     * Lake格式本质上是增强的HTML,包含语雀的特殊卡片元素
     * 使用Flexmark进行专业的HTML到Markdown的转换
     *
     * @param lakeContent Lake格式的内容(本质上是HTML)
     * @return 转换后的Markdown内容
     */
    private String convertLakeToMarkdown(String lakeContent) {
        if (lakeContent == null || lakeContent.isEmpty()) {
            return "";
        }

        try {
            // 0. 预处理: 清理语雀特殊标记
            String processed = lakeContent;

            // 移除语雀的锚点标记 {#xxxx}
            processed = processed.replaceAll("\\{#[a-z0-9]+\\}", "");

            // 处理多个连续的 <br /> 标签（替换为段落分割）
            processed = processed.replaceAll("(<br\\s*/?\\s*){2,}", "\n\n");
            processed = processed.replaceAll("<br\\s*/?\\s*>", "\n");

            // 1. 使用Jsoup解析并清理HTML
            Document doc = Jsoup.parse(processed);
            String cleanHtml = doc.body().html();

            // 2. 使用Flexmark转换为Markdown
            FlexmarkHtmlConverter converter = FlexmarkHtmlConverter.builder()
                    .build();
            String markdown = converter.convert(cleanHtml);

            // 3. 后处理: 清理多余空行和标记
            // 移除可能残留的锚点标记
            markdown = markdown.replaceAll("\\{#[a-z0-9]+\\}", "");

            // 清理多余的空行
            markdown = markdown.replaceAll("\n{3,}", "\n\n");

            // 清理行尾空格
            markdown = markdown.replaceAll("[ \t]+\n", "\n");

            markdown = markdown.trim();

            log.debug("✓ Lake格式已通过Flexmark转换为Markdown, 内容长度: {}", markdown.length());
            return markdown;

        } catch (Exception e) {
            log.warn("❌ Lake格式转换失败: {}, 尝试使用简单转换方法", e.getMessage());
            return simpleHtmlToMarkdown(lakeContent);
        }
    }

    /**
     * 将HTML格式转换为Markdown
     * 使用Flexmark进行高质量的转换,支持表格、代码块、列表等元素
     *
     * @param htmlContent HTML格式的内容
     * @return 转换后的Markdown内容
     */
    private String convertHtmlToMarkdown(String htmlContent) {
        if (htmlContent == null || htmlContent.isEmpty()) {
            return "";
        }

        try {
            // 使用Flexmark进行专业的HTML到Markdown转换
            FlexmarkHtmlConverter converter = FlexmarkHtmlConverter.builder()
                    .build();
            String markdown = converter.convert(htmlContent);

            // 清理多余的空行
            markdown = markdown.replaceAll("\n{3,}", "\n\n");
            markdown = markdown.trim();

            log.debug("✓ HTML已通过Flexmark转换为Markdown, 内容长度: {}", markdown.length());
            return markdown;

        } catch (Exception e) {
            log.warn("❌ HTML转换失败: {}, 尝试使用简单转换方法", e.getMessage());
            return simpleHtmlToMarkdown(htmlContent);
        }
    }

    /**
     * 将HTML表格转换为Markdown表格
     *
     * @param htmlContent HTML内容
     * @return 转换后的内容
     */
    private String convertTableToMarkdown(String htmlContent) {
        String result = htmlContent;

        // 使用正则表达式匹配整个表格
        // 这个正则会匹配 <table>...</table>
        java.util.regex.Pattern tablePattern = java.util.regex.Pattern.compile(
            "<table[^>]*>(.*?)</table>",
            java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE
        );

        java.util.regex.Matcher tableMatcher = tablePattern.matcher(result);
        StringBuffer sb = new StringBuffer();

        while (tableMatcher.find()) {
            String tableHtml = tableMatcher.group(1);
            String markdownTable = convertSingleTable(tableHtml);
            tableMatcher.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(markdownTable));
        }
        tableMatcher.appendTail(sb);

        return sb.toString();
    }

    /**
     * 将单个HTML表格转换为Markdown表格格式
     *
     * @param tableHtml 表格的内部HTML (不包括 <table> 标签)
     * @return Markdown 表格格式
     */
    private String convertSingleTable(String tableHtml) {
        StringBuilder markdown = new StringBuilder();

        // 提取表头行 (<thead> 或第一行 <tr>)
        java.util.regex.Pattern theadPattern = java.util.regex.Pattern.compile(
            "<thead[^>]*>(.*?)</thead>",
            java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE
        );
        java.util.regex.Matcher theadMatcher = theadPattern.matcher(tableHtml);

        String headerHtml = null;
        if (theadMatcher.find()) {
            headerHtml = theadMatcher.group(1);
        }

        // 如果没有明确的 <thead>, 从第一行 <tr> 提取
        if (headerHtml == null || headerHtml.isEmpty()) {
            java.util.regex.Pattern firstTrPattern = java.util.regex.Pattern.compile(
                "<tr[^>]*>(.*?)</tr>",
                java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE
            );
            java.util.regex.Matcher firstTrMatcher = firstTrPattern.matcher(tableHtml);
            if (firstTrMatcher.find()) {
                headerHtml = firstTrMatcher.group(1);
            }
        }

        // 处理表头行
        if (headerHtml != null && !headerHtml.isEmpty()) {
            java.util.List<String> headers = extractTableCells(headerHtml);
            if (!headers.isEmpty()) {
                markdown.append("| ").append(String.join(" | ", headers)).append(" |\n");
                markdown.append("| ").append(String.join(" | ",
                    java.util.Collections.nCopies(headers.size(), "---"))).append(" |\n");
            }
        }

        // 提取数据行 (<tbody> 或剩余的 <tr>)
        java.util.regex.Pattern tbodyPattern = java.util.regex.Pattern.compile(
            "<tbody[^>]*>(.*?)</tbody>",
            java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE
        );
        java.util.regex.Matcher tbodyMatcher = tbodyPattern.matcher(tableHtml);

        String bodyHtml = tableHtml;
        if (tbodyMatcher.find()) {
            bodyHtml = tbodyMatcher.group(1);
        }

        // 提取所有行
        java.util.regex.Pattern rowPattern = java.util.regex.Pattern.compile(
            "<tr[^>]*>(.*?)</tr>",
            java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE
        );
        java.util.regex.Matcher rowMatcher = rowPattern.matcher(bodyHtml);

        boolean isFirstRow = true;
        while (rowMatcher.find()) {
            String rowHtml = rowMatcher.group(1);
            java.util.List<String> cells = extractTableCells(rowHtml);

            if (!cells.isEmpty()) {
                // 如果这是第一行且还没有表头,跳过它(它可能已经被处理过)
                if (isFirstRow && headerHtml == null) {
                    java.util.regex.Pattern thPattern = java.util.regex.Pattern.compile(
                        "<th[^>]*>",
                        java.util.regex.Pattern.CASE_INSENSITIVE
                    );
                    if (thPattern.matcher(rowHtml).find()) {
                        // 这是表头行,跳过
                        isFirstRow = false;
                        continue;
                    }
                }

                markdown.append("| ").append(String.join(" | ", cells)).append(" |\n");
                isFirstRow = false;
            }
        }

        return markdown.toString();
    }

    /**
     * 从表行HTML中提取单元格内容
     *
     * @param rowHtml 行的HTML内容
     * @return 单元格内容列表
     */
    private java.util.List<String> extractTableCells(String rowHtml) {
        java.util.List<String> cells = new java.util.ArrayList<>();

        // 匹配 <td> 或 <th> 标签
        java.util.regex.Pattern cellPattern = java.util.regex.Pattern.compile(
            "<(td|th)[^>]*>(.*?)</(td|th)>",
            java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE
        );
        java.util.regex.Matcher cellMatcher = cellPattern.matcher(rowHtml);

        while (cellMatcher.find()) {
            String cellContent = cellMatcher.group(2);

            // 清理HTML标签
            cellContent = cellContent.replaceAll("<[^>]+>", "").trim();

            // 处理HTML实体
            cellContent = cellContent.replace("&nbsp;", " ")
                    .replace("&lt;", "<")
                    .replace("&gt;", ">")
                    .replace("&amp;", "&")
                    .replace("&quot;", "\"")
                    .replace("&#39;", "'");

            // 移除换行符 (表格单元格中不应该有换行)
            cellContent = cellContent.replace("\n", " ").replace("\r", " ").trim();

            cells.add(cellContent.isEmpty() ? "&nbsp;" : cellContent);
        }

        return cells;
    }

    /**
     * 简单的HTML到Markdown转换(备用方案)
     * 当Flexmark失败时使用此方法进行基本转换
     *
     * @param htmlContent HTML内容
     * @return 简单转换后的Markdown
     */
    private String simpleHtmlToMarkdown(String htmlContent) {
        if (htmlContent == null || htmlContent.isEmpty()) {
            return "";
        }

        String result = htmlContent;

        // 第一步: 清理语雀特殊标记
        // 移除语雀的锚点标记 {#xxxx}
        result = result.replaceAll("\\{#[a-z0-9]+\\}", "");

        // 处理多个连续的 <br /> 标签（替换为段落分割）
        result = result.replaceAll("(<br\\s*/?\\s*){2,}", "\n\n");
        result = result.replaceAll("<br\\s*/?\\s*>", "\n");

        // 标题转换 (h1-h6)
        result = result.replaceAll("<h1[^>]*>(.*?)</h1>", "# $1\n");
        result = result.replaceAll("<h2[^>]*>(.*?)</h2>", "## $1\n");
        result = result.replaceAll("<h3[^>]*>(.*?)</h3>", "### $1\n");
        result = result.replaceAll("<h4[^>]*>(.*?)</h4>", "#### $1\n");
        result = result.replaceAll("<h5[^>]*>(.*?)</h5>", "##### $1\n");
        result = result.replaceAll("<h6[^>]*>(.*?)</h6>", "###### $1\n");

        // 粗体和斜体
        result = result.replaceAll("<(strong|b)>(.*?)</\\1>", "**$2**");
        result = result.replaceAll("<(em|i)>(.*?)</\\1>", "*$2*");
        result = result.replaceAll("<u>(.*?)</u>", "__$1__");

        // 删除线
        result = result.replaceAll("<(del|s|strike)>(.*?)</\\1>", "~~$2~~");

        // 表格处理 - 必须在删除其他HTML标签之前处理
        // 将 <table> 转换为 Markdown 表格格式
        result = convertTableToMarkdown(result);

        // 代码块 (带语言标记)
        result = result.replaceAll("<pre[^>]*><code[^>]*class=\"language-([^\"]+)\"[^>]*>(.*?)</code></pre>",
            "```$1\n$2\n```");
        // 代码块 (无语言标记)
        result = result.replaceAll("<pre[^>]*><code[^>]*>(.*?)</code></pre>", "```\n$1\n```");

        // 行内代码
        result = result.replaceAll("<code>(.*?)</code>", "`$1`");

        // 链接
        result = result.replaceAll("<a[^>]+href=\"([^\"]+)\"[^>]*>(.*?)</a>", "[$2]($1)");

        // 图片
        result = result.replaceAll("<img[^>]+src=\"([^\"]+)\"[^>]+alt=\"([^\"]+)\"[^>]*>", "![$2]($1)");
        result = result.replaceAll("<img[^>]+src=\"([^\"]+)\"[^>]*>", "![]($1)");

        // 有序列表
        result = result.replaceAll("<li[^>]*>(.*?)</li>", "- $1\n");
        result = result.replaceAll("<ol[^>]*>(.*?)</ol>", "$1");
        result = result.replaceAll("<ul[^>]*>(.*?)</ul>", "$1");

        // 引用块
        result = result.replaceAll("<blockquote[^>]*>(.*?)</blockquote>", "> $1\n");

        // 段落
        result = result.replaceAll("<p[^>]*>(.*?)</p>", "$1\n\n");

        // 分割线
        result = result.replaceAll("<hr[^>]*>", "\n---\n");

        // 换行
        result = result.replaceAll("<br[^>]*>", "\n");
        result = result.replaceAll("<br\\s*/>", "\n");

        // 移除剩余的HTML标签
        result = result.replaceAll("<[^>]+>", "");

        // HTML实体解码
        result = result.replace("&nbsp;", " ")
                .replace("&lt;", "<")
                .replace("&gt;", ">")
                .replace("&amp;", "&")
                .replace("&quot;", "\"")
                .replace("&#39;", "'")
                .replace("&hellip;", "...")
                .replace("&mdash;", "—")
                .replace("&ndash;", "–");

        // 清理多余的空行
        result = result.replaceAll("\n{3,}", "\n\n");
        result = result.trim();

        return result;
    }

    /**
     * 生成默认内容(当文档内容为空时)
     * 用标题、链接和元数据生成一个基础文档
     */
    private String generateDefaultContent(JSONObject dataObject, YuqueDoc doc) {
        StringBuilder content = new StringBuilder();

        // 添加标题
        content.append("# ").append(doc.getTitle()).append("\n\n");

        // 添加文档信息
        content.append("> **文档元信息**\n\n");
        content.append("- **文档ID**: ").append(doc.getId()).append("\n");
        content.append("- **文档Slug**: ").append(doc.getSlug()).append("\n");

        // 从 dataObject 中提取额外信息
        if (dataObject.getString("description") != null) {
            content.append("- **描述**: ").append(dataObject.getString("description")).append("\n");
        }

        if (dataObject.getString("created_at") != null) {
            content.append("- **创建时间**: ").append(dataObject.getString("created_at")).append("\n");
        }

        if (dataObject.getString("updated_at") != null) {
            content.append("- **更新时间**: ").append(dataObject.getString("updated_at")).append("\n");
        }

        JSONObject creatorObj = dataObject.getJSONObject("creator");
        if (creatorObj != null && creatorObj.getString("name") != null) {
            content.append("- **创建者**: ").append(creatorObj.getString("name")).append("\n");
        }

        content.append("\n");
        content.append("---\n\n");
        content.append("**说明**: 此文档在语雀中的内容为空。以上仅显示文档的基本信息。\n");

        return content.toString();
    }

    /**
     * 导出文档为指定格式(Word/PDF等)
     * API: POST /api/docs/{id}/export
     *
     * @param doc  文档对象
     * @param type 导出类型: word, pdf, markdown 等
     * @return 导出结果,包含下载URL
     * @throws IOException 网络请求异常
     */
    public ExportResult exportDocToFormat(YuqueDoc doc, String type) throws IOException {
        long docId = doc.getId();
        String url = yuqueConfig.getBaseUrl() + "/docs/" + docId + "/export";

        log.debug("正在导出文档 [{}] 为 {} 格式", doc.getTitle(), type);
        log.debug("  - 请求URL: {}", url);

        try {
            // 构建请求体
            JSONObject requestBody = new JSONObject();
            requestBody.put("type", type);
            requestBody.put("force", 0);

            String responseString = executePost(url, requestBody.toString());
            JSONObject jsonObject = JSON.parseObject(responseString);

            JSONObject dataObject = jsonObject.getJSONObject("data");
            ExportResult result = new ExportResult();

            if (dataObject != null) {
                String state = dataObject.getString("state");
                result.setStatus(state);

                if ("success".equals(state)) {
                    String downloadUrl = dataObject.getString("url");
                    result.setUrl(downloadUrl);
                    log.debug("  - 导出成功, 下载URL: {}", downloadUrl);
                } else if ("pending".equals(state)) {
                    // 导出仍在进行中,需要轮询
                    result.setTaskId(docId + "_" + type);
                    log.debug("  - 导出进行中,需要轮询");
                }
            }

            return result;

        } catch (Exception e) {
            log.error("导出文档 [{}] 为 {} 格式失败: {}", doc.getTitle(), type, e.getMessage());
            ExportResult result = new ExportResult();
            result.setStatus("failed");
            return result;
        }
    }

    /**
     * 轮询获取导出状态
     *
     * @param docId 文档ID
     * @param type  导出类型
     * @return 导出结果
     * @throws IOException 网络请求异常
     */
    public ExportResult pollExportStatus(Long docId, String type) throws IOException {
        String url = yuqueConfig.getBaseUrl() + "/docs/" + docId + "/export";

        try {
            JSONObject requestBody = new JSONObject();
            requestBody.put("type", type);
            requestBody.put("force", 0);

            String responseString = executePost(url, requestBody.toString());
            JSONObject jsonObject = JSON.parseObject(responseString);

            JSONObject dataObject = jsonObject.getJSONObject("data");
            ExportResult result = new ExportResult();

            if (dataObject != null) {
                String state = dataObject.getString("state");
                result.setStatus(state);

                if ("success".equals(state)) {
                    String downloadUrl = dataObject.getString("url");
                    result.setUrl(downloadUrl);
                    log.debug("  - 导出完成, 下载URL: {}", downloadUrl);
                }
            }

            return result;

        } catch (Exception e) {
            log.error("轮询导出状态失败: {}", e.getMessage());
            ExportResult result = new ExportResult();
            result.setStatus("failed");
            return result;
        }
    }

    /**
     * 执行GET请求的通用方法
     *
     * @param url 请求URL
     * @return 响应内容
     * @throws IOException 网络请求异常
     */
    private String executeGet(String url) throws IOException {
        HttpGet request = new HttpGet(url);
        request.setHeader("Cookie", yuqueConfig.getAuthToken());
        request.setHeader("X-Csrf-Token", yuqueConfig.getCsrfToken());

        HttpResponse response = httpClient.execute(request);
        HttpEntity entity = response.getEntity();
        return EntityUtils.toString(entity);
    }

    /**
     * 执行POST请求的通用方法
     *
     * @param url  请求URL
     * @param body 请求体
     * @return 响应内容
     * @throws IOException 网络请求异常
     */
    private String executePost(String url, String body) throws IOException {
        HttpPost request = new HttpPost(url);
        request.setHeader("Cookie", yuqueConfig.getAuthToken());
        request.setHeader("X-Csrf-Token", yuqueConfig.getCsrfToken());
        request.setHeader("Content-Type", "application/json");

        request.setEntity(new StringEntity(body, "UTF-8"));

        HttpResponse response = httpClient.execute(request);
        HttpEntity entity = response.getEntity();
        return EntityUtils.toString(entity);
    }
}
